/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.vwbe.impl;

import elaborate.vwbe.VWBEApplication;
import elaborate.vwbe.VWBEApplicationBuilder;
import elaborate.vwbe.annotation.*;
import elaborate.vwbe.annotation.impl.*;
import elaborate.vwbe.resource_fetcher.ResourceFetcher;
import elaborate.vwbe.scripting.ScriptLanguage;
import elaborate.vwbe.scripting.ScriptingException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author lendle
 */
public class DefaultVWBEApplicationBuilder implements VWBEApplicationBuilder {

    private String charset = "utf-8";

    public VWBEApplication createVWBEApplication(URL htmlPage) throws Exception {
        ResourceFetcher resourceFetcher = new DefaultResourceFetcher();
        Document doc = resourceFetcher.fetchDOMResource(htmlPage, charset);
        DefaultVWBEContext context = new DefaultVWBEContext();
        context.setBaseURL(htmlPage);
        context.setCharset(charset);
        context.setHTMLDocument(doc);
        DefaultVWBEApplication application = new DefaultVWBEApplication();
        this.initializeJavaScript(context);
        this.initializeAJAX(context);
        application.init(context);
        return application;
    }

    public void setCharacterEncoding(String charset) {
        this.charset = charset;
    }

    protected void initializeJavaScript(DefaultVWBEContext context) throws ScriptingException {
        Document doc = context.getHTMLDocument();
        NodeList scriptElements = doc.getElementsByTagName("script");
        for (int i = 0; i < scriptElements.getLength(); i++) {
            Element scriptElement = (Element) scriptElements.item(i);
            if ("text/javascript".equals(scriptElement.getAttribute("type"))) {
                //check for @export annotation, only eval script elements with
                //an @export annotation declared (int a comment region right
                //before it)
                String scriptContent = getScriptText(context, scriptElement);
                Logger.getLogger(this.getClass().getName()).log(Level.FINEST, "scriptContent="+scriptContent);
                StringReader input = new StringReader(scriptContent);
                CommentTargetPairParser parser = new CommentTargetPairParser(input);
                AnnotationAnalyzer annotationAnalyzer=new DefaultAnnotationAnalyzer();
                
                try {
                    List<CodeSegment> pairs = parser.Start();
                    List<AnnotatedContent> annotatedContents=annotationAnalyzer.analyze(pairs);
                    for (AnnotatedContent annotatedContent : annotatedContents) {
                        List<Annotation> annotations = annotatedContent.getAnnotations();
                        //check for annotation
                        for (Annotation annotation : annotations) {
                            //System.out.println(annotation.getName());
                            if ("export".equals(annotation.getName())) {
                                //this is the part to be exported
                                String _scriptContent = annotatedContent.getContent();
                                context.getScriptEngine(ScriptLanguage.JavaScript).evalScripts(_scriptContent);
                                Logger.getLogger(this.getClass().getName()).info("evaluated: "+_scriptContent);
                                break;
                            }
                        }
                    }
                } catch (ParseException ex) {
                    Logger.getLogger(DefaultVWBEApplicationBuilder.class.getName()).log(Level.SEVERE, null, ex);
                    throw new ScriptingException(ex);
                } catch(AnalyzerException ex){
                    Logger.getLogger(DefaultVWBEApplicationBuilder.class.getName()).log(Level.SEVERE, null, ex);
                    throw new ScriptingException(ex);
                }
            }
        }
    }
    /**
     * initialize AJAX implementation resources (specifically, the ajax.js file)
     * @param context
     * @throws ScriptingException 
     */
    protected void initializeAJAX(DefaultVWBEContext context) throws ScriptingException {
        BufferedReader input=null;
        try {
            input=new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResource("elaborate/vwbe/impl/javascript/ajax.js").openStream()));
            StringBuffer buffer=new StringBuffer();
            String code=input.readLine();
            while(code!=null){
                if(buffer.length()>0){
                    buffer.append("\r\n");
                }
                buffer.append(code);
                code=input.readLine();
            }
            context.getScriptEngine(ScriptLanguage.JavaScript).evalScripts(buffer.toString());
        } catch (IOException ex) {
            Logger.getLogger(DefaultVWBEApplicationBuilder.class.getName()).log(Level.SEVERE, null, ex);
            throw new ScriptingException(ex);
        }
        finally{
            if(input!=null){
                try {
                    input.close();
                } catch (IOException ex) {
                    Logger.getLogger(DefaultVWBEApplicationBuilder.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    private static String getScriptText(DefaultVWBEContext context, Element scriptElement) {
        if (scriptElement.hasAttribute("src") == false) {
            Node child = scriptElement.getFirstChild();
            while (child != null) {
                if (child.getNodeType() == Node.CDATA_SECTION_NODE) {
                    CDATASection cdata = (CDATASection) child;
                    return cdata.getNodeValue();
                }
                child = child.getNextSibling();
            }
        }
        else{
            //load javascript from the specified url
            String src=scriptElement.getAttribute("src");
            try {
                URL jsURL=new URL(context.getBaseURL(), src);
                ResourceFetcher resourceFetcher = new DefaultResourceFetcher();
                return resourceFetcher.fetchStringResource(jsURL, context.getCharset());
            } catch (Exception ex) {
                Logger.getLogger(DefaultVWBEApplicationBuilder.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }
    
}
